/*
 * Title:        CloudSim Toolkit
 * Description:  CloudSim (Cloud Simulation) Toolkit for Modeling and Simulation
 *               of Clouds
 * Licence:      GPL - http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2009, The University of Melbourne, Australia
 */

package org.cloudbus.cloudsim.examples;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;

import org.cloudbus.cloudsim.Cloudlet;
import org.cloudbus.cloudsim.CloudletSchedulerTimeShared;
import org.cloudbus.cloudsim.Datacenter;
import org.cloudbus.cloudsim.DatacenterBroker;
import org.cloudbus.cloudsim.DatacenterCharacteristics;
import org.cloudbus.cloudsim.Host;
import org.cloudbus.cloudsim.Log;
import org.cloudbus.cloudsim.Pe;
import org.cloudbus.cloudsim.Storage;
import org.cloudbus.cloudsim.UtilizationModel;
import org.cloudbus.cloudsim.UtilizationModelFull;
import org.cloudbus.cloudsim.Vm;
import org.cloudbus.cloudsim.VmAllocationPolicySimple;
import org.cloudbus.cloudsim.VmSchedulerSpaceShared;
import org.cloudbus.cloudsim.core.CloudSim;
import org.cloudbus.cloudsim.provisioners.BwProvisionerSimple;
import org.cloudbus.cloudsim.provisioners.PeProvisionerSimple;
import org.cloudbus.cloudsim.provisioners.RamProvisionerSimple;

 //怎样创建2个数据中心，各含1台主机，并在其上运行二个用户的云任务
public class CloudSimExample5 {

	//创建两个云任务列表
	private static List<Cloudlet> cloudletList1;
	private static List<Cloudlet> cloudletList2;

	//创建两个虚拟机列表
	private static List<Vm> vmlist1;
	private static List<Vm> vmlist2;

	public static void main(String[] args) {

		Log.printLine("Starting CloudSimExample5...");

		try {
			//******************初始化工具包***********************************************************
			int num_user = 2; //两个用户
			Calendar calendar = Calendar.getInstance();
			boolean trace_flag = false;  
			CloudSim.init(num_user, calendar, trace_flag);
			//**************************************************************************************
	
			
			//******************创建两个数据中心********************************************************
			@SuppressWarnings("unused")
			Datacenter datacenter0 = createDatacenter("Datacenter_0");
			@SuppressWarnings("unused")
			Datacenter datacenter1 = createDatacenter("Datacenter_1");
			//**************************************************************************************
			
			
			//*******************创建两个代理**********************************************************
			DatacenterBroker broker1 = createBroker(1);
			int brokerId1 = broker1.getId();//获得代理的ID，在后边创建虚拟机时要用
			DatacenterBroker broker2 = createBroker(2);
			int brokerId2 = broker2.getId();
			//**************************************************************************************

			
			//********************配置虚拟机***********************************************************
			vmlist1 = new ArrayList<Vm>();      //上边已经声明了，在这new一个个虚拟机列表
			vmlist2 = new ArrayList<Vm>();
			int vmid = 0;                       //虚拟机ID
			int mips = 250;				        //运行速度
			long size = 10000;                  //外存(MB)
			int ram = 512;                      //内存(MB)
			long bw = 1000;                     //带宽
			int pesNumber = 1;                  //CPU个数    在下边创建云任务时要用到
			String vmm = "Xen";                 //虚拟机管理程序的名字
			//创建两个虚拟机对象,将代理的ID传入虚拟机，这里用到代理ID
			Vm vm1 = new Vm(vmid, brokerId1, mips, pesNumber, ram, bw, size, vmm, new CloudletSchedulerTimeShared());
			Vm vm2 = new Vm(vmid, brokerId2, mips, pesNumber, ram, bw, size, vmm, new CloudletSchedulerTimeShared());
			//把虚拟机添加到虚拟机列表
			vmlist1.add(vm1);
			vmlist2.add(vm2);
			//把虚拟机列表提交给代理1，代理2
			broker1.submitVmList(vmlist1);
			broker2.submitVmList(vmlist2);
			//******************************************************************************************
			
			
			//*************云任务属性（即完成这个云任务所需要的配置）**********************************************
			cloudletList1 = new ArrayList<Cloudlet>();   //上边已经声明了，在这new一个个云任务列表
			cloudletList2 = new ArrayList<Cloudlet>();
			int id = 0;                                  //云任务ID
			long length = 40000;                         //执行任务请求的PE数
			long fileSize = 300;                         //云任务文件大小
			long outputSize = 300;                       //云任务输出文件的大小
			//？？？？？？？此代码作用未知？但在创建云任务对象时要作为参数传入？？？？？？？？
			UtilizationModel utilizationModel = new UtilizationModelFull();
			//创建云任务对象，这里要用到上边的pesNumber
			Cloudlet cloudlet1 = new Cloudlet(id, length, pesNumber, fileSize, outputSize, utilizationModel, utilizationModel, utilizationModel);
			cloudlet1.setUserId(brokerId1);
			Cloudlet cloudlet2 = new Cloudlet(id, length, pesNumber, fileSize, outputSize, utilizationModel, utilizationModel, utilizationModel);
			cloudlet2.setUserId(brokerId2);
			//把云任务添加进云任务列表
			cloudletList1.add(cloudlet1);
			cloudletList2.add(cloudlet2);
			//把云任务列表提交给代理
			broker1.submitCloudletList(cloudletList1);
			broker2.submitCloudletList(cloudletList2);
			//*******************************************************************************************
			
			
			//**********************开始并获取结果***********************************************************
			CloudSim.startSimulation();//开始仿真
			//获取代理1和2的结果，并用一个新建的Cloudlet类型的列表接收结果
			List<Cloudlet> newList1 = broker1.getCloudletReceivedList();
			List<Cloudlet> newList2 = broker2.getCloudletReceivedList();		
			CloudSim.stopSimulation(); //结束仿真
			//打印列表的内容
			Log.print("=============> User "+brokerId1+"    ");         
			printCloudletList(newList1);
			Log.print("=============> User "+brokerId2+"    ");
			printCloudletList(newList2);
			Log.printLine("CloudSimExample5 finished!");
			//*******************************************************************************************
		}
		catch (Exception e) {
			e.printStackTrace();
			Log.printLine("The simulation has been terminated due to an unexpected error");
		}
	}
	
	
	
//**************************************创建数据中心的函数***************************************************
	private static Datacenter createDatacenter(String name){

		//************************创建主机*********************************
		List<Host> hostList = new ArrayList<Host>();//创建列表存储机器
		List<Pe> peList = new ArrayList<Pe>();      //创建列表存储CPU	
		int mips=1000;                              //CPU运行速度
		//创建CPU对象并添加进CPU列表，各参数是（CPU的ID，处理器速度）
		peList.add(new Pe(0, new PeProvisionerSimple(mips)));
		//创建主机
		int hostId=0;                    //主机ID
		int ram = 2048;                  //主机内存(MB)
		long storage = 1000000;          //存储空间大小
		int bw = 10000;                  //带宽
		//创建主机对象并把主机对象添加进主机列表，各参数是（主机ID，主机内存，带宽，存储空间，CPU列表，虚拟机空间共享）
		hostList.add(new Host(hostId,new RamProvisionerSimple(ram),new BwProvisionerSimple(bw),storage,peList,new VmSchedulerSpaceShared(peList))); 
    	//***************************************************************
		
		
		//*********************配置数据中心各项参数***************************
		String arch = "x86";             // 系统架构
		String os = "Linux";             // 操作系统
		String vmm = "Xen";              // 虚拟机管理程序
		double time_zone = 10.0;         // 这个资源所在的时区
		double cost = 3.0;               // 处理成本
		double costPerMem = 0.05;		 // 内存成本
		double costPerStorage = 0.001;	 // 存储成本
		double costPerBw = 0.0;			 // 带宽成本
		LinkedList<Storage> storageList = new LinkedList<Storage>();//存储空间列表。暂时不添加SAN（存储区域网）
		//创建一个对象来存储这繁多的参数
		DatacenterCharacteristics characteristics = new DatacenterCharacteristics(
                arch, os, vmm, hostList, time_zone, cost, costPerMem, costPerStorage, costPerBw);
		// 创建数据中心对象，并返回一个数据中心对象
		Datacenter datacenter = null;
		try {
			datacenter = new Datacenter(name, characteristics, new VmAllocationPolicySimple(hostList), storageList, 0);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//*****************************************************************
		return datacenter;
	}
//******************************************************************************************************

	
	
//******************************************创建代理******************************************************
	private static DatacenterBroker createBroker(int id){

		DatacenterBroker broker = null;
		try {
			broker = new DatacenterBroker("Broker"+id);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return broker;
	}
//*******************************************************************************************************
	
	
	
//*******************************************打印结果******************************************************	
	/**
	 * Prints the Cloudlet objects
	 * @param list  list of Cloudlets
	 */
	private static void printCloudletList(List<Cloudlet> list) {
		int size = list.size();
		Cloudlet cloudlet;
		String indent = "    ";
		Log.printLine();
		Log.printLine("========== OUTPUT ==========");
		Log.printLine("Cloudlet ID" + indent + "STATUS" + indent +
				"Data center ID" + indent + "VM ID" + indent + "Time" + indent + "Start Time" + indent + "Finish Time");
		DecimalFormat dft = new DecimalFormat("###.##");
		for (int i = 0; i < size; i++) {
			cloudlet = list.get(i);
			Log.print(indent + cloudlet.getCloudletId() + indent + indent);
			if (cloudlet.getCloudletStatus() == Cloudlet.SUCCESS){
				Log.print("SUCCESS");
				Log.printLine( indent + indent + cloudlet.getResourceId() + indent + indent + indent + cloudlet.getVmId() +
						indent + indent + dft.format(cloudlet.getActualCPUTime()) + indent + indent + dft.format(cloudlet.getExecStartTime())+
						indent + indent + dft.format(cloudlet.getFinishTime()));
			}
		}

	}
//********************************************************************************************************
}
